﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Windows.Input;

using log4net;

using WeatherReport.DataModel;
using WeatherReport.Interfaces;
using WeatherReport.MVVM;

namespace WeatherReport.ViewModels
{
    public class WeatherViewModel : ObservableObject
    {
        public const string PROP_WEATHER_INFO = "WeatherInfo";

        private const string DEFAULT_DISPLAYED_CITY = "----";        

        private readonly IWeatherService _weatherService;
        private readonly ILog _logger;
        private IWeatherSettings _settings;
        private readonly Timer _serviceRefreshTimer;

        private bool _isSettingsLayerVisible;        

        private RelayCommand _settingsCommand;       
        
        private WeatherInfo _weatherInfo;
        private RegionInfo _displayedRegion;
        private string _displayedCity;
        private string _infoDisplayString = DEFAULT_DISPLAYED_CITY;

        private InfoDisplayStatus _infoDisplayStatus = InfoDisplayStatus.Normal;

        public WeatherInfo WeatherInfo
        {
            get { return _weatherInfo; }
            set
            {
                if (!_weatherInfo.Equals(value))
                {
                    _weatherInfo = value;
                    RaisePropertyChanged(PROP_WEATHER_INFO);
                    RaisePropertyChanged("TemperatureString");
                }
            }
        }
        
        public RegionInfo DisplayedRegion
        {
            get { return _displayedRegion; }
            set
            {
                if (_displayedRegion != value)
                {
                    _displayedRegion = value;
                    RaisePropertyChanged("DisplayedRegion");
                }
            }
        }

        public string DisplayedCity
        {
            get { return _displayedCity; }
            set
            {
                if (_displayedCity != value)
                {
                    _displayedCity = value;
                    RaisePropertyChanged("DisplayedCity");
                }
            }
        }

        public bool IsSettingsLayerVisible
        {
            get { return _isSettingsLayerVisible; }
            set
            {
                if (_isSettingsLayerVisible != value)
                {
                    _isSettingsLayerVisible = value;
                    RaisePropertyChanged("IsSettingsLayerVisible");
                    RaisePropertyChanged("IsSettingsButtonVisible");
                }
            }
        }

        public bool IsSettingsButtonVisible
        {
            get { return !_isSettingsLayerVisible; }
        }

        public string TemperatureString
        {
            get
            {
                return _weatherInfo.Temperature.HasValue ?
                    String.Format("{0,4:0.0}°C", _weatherInfo.Temperature.Value) : "--.-°C";
            }
        }

        public string InfoDisplayString
        {
            get { return _infoDisplayString; }
            set
            {
                if (_infoDisplayString != value)
                {
                    _infoDisplayString = value;
                    RaisePropertyChanged("InfoDisplayString");
                }
            }
        }

        public InfoDisplayStatus InfoDisplayStatus
        {
            get { return _infoDisplayStatus; }
            set
            {
                if (_infoDisplayStatus != value)
                {
                    _infoDisplayStatus = value;
                    RaisePropertyChanged("InfoDisplayStatus");
                }
            }
        }

        public double WindSpeed
        {
            get { return _weatherInfo.WindSpeed ?? 0.0; }
        }

        public double WindDirection
        {
            get { return _weatherInfo.WindDirection ?? 0.0; }
        }

        public ICommand SettingsCommand
        {
            get { return _settingsCommand; }
        }

        public WeatherViewModel(IWeatherService weatherService, ILog logger, IWeatherSettings settings)
        {
            if (weatherService == null)
                throw new ArgumentNullException("weatherService");
            if (settings == null)
                throw new ArgumentNullException("settings");

            _weatherService = weatherService;
            _logger = logger;
            _settings = settings;

            _settingsCommand = new RelayCommand(SettingsButton_Clicked);

            _weatherInfo = WeatherInfo.Empty;            

            int refreshMs = 1000 * settings.RefreshIntervalSeconds;
            _serviceRefreshTimer = new Timer(callback => GetWeather(), null, refreshMs, refreshMs);      
        }

        public void Init()
        {
            if (!String.IsNullOrEmpty(_settings.SelectedCity))
                _displayedCity = _settings.SelectedCity;

            if (!String.IsNullOrEmpty(_settings.SelectedCountry))
            {
                try
                {
                    _displayedRegion = new RegionInfo(_settings.SelectedCountry);
                }
                catch (ArgumentException) 
                {
                    _logger.WarnFormat("Incorrect country {0} stored in user settings", _settings.SelectedCountry);
                }

                RunGetWeather();
            }
            else
                IsSettingsLayerVisible = true;
        }

        public void ChangeLocation(RegionInfo newRegion, string newCity)
        {
            DisplayedRegion = newRegion;
            DisplayedCity = newCity;
            IsSettingsLayerVisible = false;
            RunGetWeather();
        }

        public void HideSettingsLayer()
        {
            IsSettingsLayerVisible = false;
        }

        private void SettingsButton_Clicked()
        {
            IsSettingsLayerVisible = true;
        }

        private void RunGetWeather()
        {
            Thread update = new Thread(new ThreadStart(GetWeather));
            update.Start();
        }

        private void GetWeather()
        {
            if (DisplayedRegion != null && DisplayedCity != null)
            {
                try
                {
                    WeatherInfo = _weatherService.GetWeather(DisplayedCity, DisplayedRegion.EnglishName);
                    ResetInfoDisplay();
                }
                catch (Services.WeatherServiceException wex)
                {
                    WeatherInfo = WeatherInfo.Empty;
                    SetInfoDisplayError(GetWeatherServiceExceptionDisplayedMessage(wex));
                    _logger.Error("GetWeather: Failed to retrieve weather info", wex);
                }                
            }
        }        

        private void SetInfoDisplayError(string message)
        {
            InfoDisplayStatus = ViewModels.InfoDisplayStatus.Error;
            InfoDisplayString = message;
        }

        private void ResetInfoDisplay()
        {
            InfoDisplayStatus = ViewModels.InfoDisplayStatus.Normal;
            InfoDisplayString = DisplayedCity ?? DEFAULT_DISPLAYED_CITY;
        }

        private string GetWeatherServiceExceptionDisplayedMessage(Services.WeatherServiceException wex)
        {
            return DisplayedErrors.ResourceManager.GetString(wex.Reason.ToString());
        }
    }
}

